#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SD_PORT        10086
#define SD_BACK_LOG    10
#define SD_MAX_CONN    10
#define sd_MAX_CONN    2000

typedef struct
{
    int      buf_len;
    char*    buf;
}
sd_conn_state;

typedef struct
{
    int                   id;
    int                   fd;
    struct sockaddr_in    addr;

    sd_conn_state*        state;
}
sd_conn;

typedef struct
{
    int         max_fd;
    int         listener_fd;

    fd_set      read_fds;
    fd_set      can_read_fds;

    fd_set      write_fds;
    fd_set      can_write_fds;

    int         conn_max;
    int         conn_free;
    int         conn_count;

    sd_conn*    conn_items;
}
sd_state;

void
sd_init (sd_state* state)
{
    int i;

    sd_conn* conn;

    int reuse = 1;

    struct sockaddr_in addr;

    if ((state->listener_fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("Create listener socket failed");
        exit(1);
    }

    if (setsockopt(state->listener_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    {
        perror("Setup listener socket failed");
        exit(1);
    }

    bzero(&(addr.sin_zero), 8);

    addr.sin_family      = AF_INET;
    addr.sin_port        = htons(SD_PORT);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(state->listener_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {
        perror("Bind listener socket address failed");
        exit(1);
    }

    if (listen(state->listener_fd, SD_BACK_LOG) == -1)
    {
        perror("Listen port failed");
        exit(1);
    }

    state->max_fd = state->listener_fd;

    FD_ZERO(&state->read_fds);
    FD_ZERO(&state->can_read_fds);

    FD_ZERO(&state->write_fds);
    FD_ZERO(&state->can_write_fds);

    FD_SET(state->listener_fd, &state->read_fds);

    state->conn_max   = -1;
    state->conn_free  = SD_MAX_CONN;
    state->conn_count = SD_MAX_CONN;
    state->conn_items = calloc(SD_MAX_CONN, sizeof(sd_conn));

    for (i = 0; i < SD_MAX_CONN; i++)
    {
        conn = &state->conn_items[i];

        conn->id = -1;
        conn->fd = -1;
        conn->state = NULL;
    }
}

void
sd_conn_accept (sd_state* state)
{
    int i;

    int addr_size;

    sd_conn* conn;

    for (i = 0; i < state->conn_count; i ++)
    {
        conn = &state->conn_items[i];

        if (conn->fd >= 0)
            continue;

        conn->id = i;
        conn->state = NULL;

        addr_size = sizeof(conn->addr);

        conn->fd = accept(state->listener_fd, (struct sockaddr *)&conn->addr, &addr_size);

        if (conn->fd > state->max_fd)
            state->max_fd = conn->fd;

        if (conn->id > state->conn_max)
            state->conn_max = conn->id;

        FD_SET(conn->fd, &state->read_fds);

        state->conn_free --;

        printf("Client connected\n");

        break;
    }
}

void
sd_conn_close (sd_state* state, sd_conn* conn)
{
    int i;

    sd_conn* temp_conn;

    if (conn->id == state->conn_max)
    {
        for (i = conn->id - 1; i >= 0; i --)
        {
            if (i < 0)
            {
                state->conn_max = -1;

                break;
            }

            temp_conn = &state->conn_items[i];

            if (temp_conn->fd > 0)
            {
                state->conn_max = temp_conn->id;

                break;
            }
        }
    }

    if (conn->fd == state->max_fd)
    {
        if (state->conn_max >= 0)
        {
            state->max_fd = state->listener_fd;

            for (i = 0; i <= state->conn_max; i ++)
            {
                temp_conn = &state->conn_items[i];

                if (temp_conn->fd > state->max_fd)
                {
                    state->max_fd = temp_conn->fd;
                }
            }
        }
    }

    FD_CLR(conn->fd, &state->read_fds);
    FD_CLR(conn->fd, &state->write_fds);

    close(conn->fd);

    conn->id = -1;
    conn->fd = -1;

    if (conn->state != NULL)
    {
        free(conn->state);
    }

    state->conn_free ++;

    printf("Client close\n");
}

void
sd_conn_proc (sd_state* state, sd_conn* conn)
{
    int ret;

    char* buf;

    buf = calloc(1024, sizeof(char));

    if ((ret = read(conn->fd, buf, 1024)) == 0)
    {
        sd_conn_close(state, conn);

        free(buf);
    }
    else
    {
        conn->state = calloc(1, sizeof(sd_conn_state));

        conn->state->buf = buf;
        conn->state->buf_len = ret;

        FD_SET(conn->fd, &state->write_fds);
    }
}

void
sd_conn_repo (sd_state* state, sd_conn* conn)
{
    if (conn->state == NULL)
        return;

    write(conn->fd, conn->state->buf, conn->state->buf_len);

    free(conn->state->buf);
    free(conn->state);

    conn->state = NULL;

    FD_CLR(conn->fd, &state->write_fds);
}

void
sd_loop (sd_state* state)
{
    int i = 0;

    sd_conn* conn;

    for (;;)
    {
        state->can_read_fds = state->read_fds;
        state->can_write_fds = state->write_fds;

        int num_ready = select(state->max_fd + 1, &state->can_read_fds, &state->can_write_fds, NULL, NULL);

        if (num_ready == 0)
            continue;

        if (num_ready == -1)
        {
            perror("Select failed\n");
            exit(1);
        }

        if (FD_ISSET(state->listener_fd, &state->can_read_fds))
        {
            sd_conn_accept(state);

            if (-- num_ready == 0)
                continue;
        }

        for (i = 0; i <= state->conn_max; i ++)
        {
            conn = &state->conn_items[i];

            if (conn->fd == -1)
                continue;

            if (FD_ISSET(conn->fd, &state->can_read_fds))
            {
                sd_conn_proc(state, conn);

                if (-- num_ready == 0)
                    break;
            }

            if (FD_ISSET(conn->fd, &state->can_write_fds))
            {
                sd_conn_repo(state, conn);

                if (-- num_ready == 0)
                    break;
            }
        }
    }
}

void
sd_down (sd_state* state)
{
    close(state->listener_fd);

    printf("Server shutdown\n");
}

int
main (int argc, char *argv[])
{
    sd_state state;

    sd_init(&state);

    sd_loop(&state);

    sd_down(&state);

    return 1;
}
